home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Snippets / Stuart's Tech Notes / StUU / Sources / DSUserProcs.c < prev    next >
Text File  |  1996-04-02  |  41KB  |  1,248 lines

  1. /******************************************************************************
  2. **
  3. **  Project Name:    DropShell
  4. **     File Name:    DSUserProcs.c
  5. **
  6. **   Description:    Specific AppleEvent handlers used by the DropBox
  7. **
  8. *******************************************************************************
  9. **                       A U T H O R   I D E N T I T Y
  10. *******************************************************************************
  11. **
  12. **    Initials    Name
  13. **    --------    -----------------------------------------------
  14. **    LDR            Leonard Rosenthol
  15. **    MTC            Marshall Clow
  16. **    SCS            Stephan Somogyi
  17. **
  18. *******************************************************************************
  19. **                      R E V I S I O N   H I S T O R Y
  20. *******************************************************************************
  21. **
  22. **      Date        Time    Author    Description
  23. **    --------    -----    ------    ---------------------------------------------
  24. **    06/23/94            LDR        Added support for ProcessItem and ProcessFolder handling
  25. **    02/20/94            LDR        Modified Preflight & Postflight to take item count
  26. **    01/25/92            LDR        Removed the use of const on the userDataHandle
  27. **    12/09/91            LDR        Added the new SelectFile userProc
  28. **                                Added the new Install & DisposeUserGlobals procs
  29. **                                Modified PostFlight to only autoquit on odoc, not pdoc
  30. **    11/24/91            LDR        Added the userProcs for pdoc handler
  31. **                                Cleaned up the placement of braces
  32. **                                Added the passing of a userDataHandle
  33. **    10/29/91            SCS        Changes for THINK C 5
  34. **    10/28/91            LDR        Officially renamed DropShell (from QuickShell)
  35. **                                Added a bunch of comments for clarification
  36. **    10/06/91    00:02    MTC        Converted to MPW C
  37. **    04/09/91    00:02    LDR        Added to Projector
  38. **
  39. ******************************************************************************/
  40.  
  41. #include <StandardFile.h>
  42. #include <Timer.h>            // For timing
  43.  
  44. #include <ICTypes.h>
  45. #include <ICKeys.h>
  46. #include <ICAPI.h>
  47.  
  48. #include "DSGlobals.h"
  49. #include "DSUserProcs.h"
  50.  
  51. #ifdef __MC68K__
  52. #include <StuTypes.h>
  53. #define WideAdd(A,B)
  54. #define WideSubtract(A,B)
  55. #endif
  56.  
  57. // Static Prototypes
  58. static OSErr ProcessItem(FSSpecPtr myFSSPtr);
  59. static OSErr ProcessFolder(FSSpecPtr myFSSPtr);
  60.  
  61.  
  62. /*
  63.     Uncomment this line if you want each item of a dropped folder processed
  64.     as an individual item
  65. */
  66. #define qWalkFolders
  67.  
  68.  
  69. /*
  70.     This routine is called during init time.
  71.     
  72.     It allows you to install more AEVT Handlers beyond the standard four
  73. */
  74. #pragma segment Main
  75. pascal void InstallOtherEvents(void) {
  76. }
  77.  
  78.  
  79. /*    
  80.     This routine is called when an OAPP event is received.
  81.     
  82.     Currently, all it does is set the gOApped flag, so you know that
  83.     you were called initally with no docs, and therefore you shouldn't 
  84.     quit when done processing any following odocs.
  85. */
  86. #pragma segment Main
  87. pascal void OpenApp(void) {
  88.     gOApped = true;
  89. }
  90.  
  91.  
  92. /*    
  93.     This routine is called when an QUIT event is received.
  94.     
  95.     We simply set the global done flag so that the main event loop can
  96.     gracefully exit.  We DO NOT call ExitToShell for two reasons:
  97.     1) It is a pretty ugly thing to do, but more importantly
  98.     2) The Apple event manager will get REAL upset!
  99. */
  100. #pragma segment Main
  101. pascal void QuitApp(void) {
  102.     gDone = true;    /*    All Done! */
  103. }
  104.  
  105.  
  106. /*    
  107.     This routine is the first one called when an ODOC or PDOC event is received.
  108.     
  109.     In this routine you would place code used to setup structures, etc. 
  110.     which would be used in a 'for all docs' situation (like "Archive all
  111.     dropped files")
  112.  
  113.     Obviously, the opening boolean tells you whether you should be opening
  114.     or printing these files based on the type of event recieved.
  115.     
  116.     NEW IN 2.0!
  117.     The itemCount parameter is simply the number of items that were dropped on
  118.     the application and that you will be processing.  This gives you the ability
  119.     to do a single preflight for memory allocation needs, rather than doing it
  120.     once for each item as in previous versions.
  121.     
  122.     userDataHandle is a handle that you can create & use to store your own
  123.     data structs.  This dataHandle will be passed around to the other 
  124.     odoc/pdoc routines so that you can get at your data without using
  125.     globals - just like the new StandardFile.  
  126.     
  127.     We also return a boolean to tell the caller if you support this type
  128.     of event.  By default, our dropboxes don't support the pdoc, so when
  129.     opening is FALSE, we return FALSE to let the caller send back the
  130.     proper error code to the AEManager.
  131.  
  132.     You will probably want to remove the #pragma unused (currently there to fool the compiler!)
  133. */
  134.  
  135. #define DO_TIMING 0
  136.  
  137. #define IsError(X) ((X) !=1 && (X) != 0)
  138. #define Alert_NoMemory  130
  139. #define Alert_NoIC      131
  140. #define Alert_DiskError 132
  141.  
  142. #if DO_TIMING
  143. static UnsignedWide total_start_time, total_end_time;
  144. static UnsignedWide create_start_time, create_end_time, create_time;
  145. static UnsignedWide write_start_time, write_end_time, write_time;
  146. static UnsignedWide close_start_time, close_end_time, close_time;
  147. static UnsignedWide read_start_time, read_end_time, read_time;
  148. #endif
  149.  
  150. typedef struct { HParamBlockRec io; void *GlobalsReg; } ex_HParamBlockRec;
  151.  
  152. static Handle InBuffer;                    // Handle to input buffer
  153. static Handle OutBuffer1, OutBuffer2;    // Handles to output output buffers
  154. static Handle OutBuffer;                // Handle to current output buffer
  155. static long InHandleSize;                // Size of input buffer
  156. static long OutHandleSize;                // Size of output buffer
  157. static short ReadRefNum;                // The file being read
  158. static long BytesRemaining;                // Bytes of file still unread
  159. static Ptr InPtr = NULL;                // Next character to read from buffer
  160. static Ptr InEnd = NULL;                // End of data in buffer
  161. static char *CurrentLine;                // Pointer to current line in the inbuffer
  162. static int  LineLength;                    // Length of current line
  163. static char TempLineBuffer[256];        // Temp holding buffer for lines spanning buffers
  164. static Ptr OutPtr;                        // Next byte to written to buffer
  165. static Ptr OutEnd;                        // End output buffer
  166. static WindowPtr StatusWindow;
  167. static ICInstance IC_instance = NULL;
  168. static Handle     IC_mappings = NULL;
  169. static ex_HParamBlockRec Async_Create_PB;
  170. static HParamBlockRec Async_Info_PB, Async_Open_PB;
  171. static ParamBlockRec Async_Write_PB, Async_Close_PB;
  172. static volatile Boolean FileBeingCreated;
  173. static FSSpec outputSpec, writeSpec, closeSpec;
  174. static unsigned char output_namesuffix;
  175. static unsigned char output_namesuffix_length;
  176. static OSType output_type, output_creator;
  177. static const Point zeroPoint;
  178.  
  179. static void MakeStatusWindow(void)
  180.     {
  181.     Rect bounds;
  182.     short screencentre = (qd.screenBits.bounds.right - qd.screenBits.bounds.left) / 2;
  183.     bounds.top    = 100;
  184.     bounds.bottom = 290;
  185.     bounds.left   = screencentre - 200;
  186.     bounds.right  = screencentre + 200;
  187.     StatusWindow = NewWindow(NULL, &bounds, NULL, FALSE, dBoxProc, (WindowPtr)-1, FALSE, 0);
  188.     }
  189.  
  190. static void PaintStatusWindow(void)
  191.     {
  192.     SetPort(StatusWindow);
  193.     TextSize(12);
  194.     TextFace(bold);
  195.     MoveTo(20, 30); DrawString("\pFast, Simple, Stupid UUDecoder");
  196.     MoveTo(20, 50); DrawString("\pby Stuart Cheshire <cheshire@cs.stanford.edu>");
  197.     MoveTo(20, 70); DrawString("\pThis software is free.");
  198.     MoveTo(20, 90); DrawString("\pYou can do whatever you like with it.");
  199.     TextFace(0);
  200.     MoveTo(20,110); DrawString("\pBuilt using DropShell 2.0");
  201.     MoveTo(20,130); DrawString("\pby Leonard Rosenthol, Marshall Clow, and Stephan Somogyi");
  202.     }
  203.  
  204. static void UpdateStatusWindowInput(unsigned char *inputname)
  205.     {
  206.     Rect r = StatusWindow->portRect;
  207.     r.top    = 135;
  208.     r.bottom = 155;
  209.     r.left   = 20;
  210.     EraseRect(&r);
  211.     TextFace(bold);
  212.     MoveTo(20,150); DrawString("\pReading File: "); DrawString(inputname);
  213.     }
  214.  
  215. static void UpdateStatusWindowOutput(unsigned char *outputname)
  216.     {
  217.     Rect r = StatusWindow->portRect;
  218.     r.top    = 155;
  219.     r.left   = 20;
  220.     EraseRect(&r);
  221.     TextFace(bold);
  222.     MoveTo(20,170); DrawString("\pWriting File: "); DrawString(outputname);
  223.     }
  224.  
  225. static void UpdateStatusWindow(Boolean reading)
  226.     {
  227.     Rect r = StatusWindow->portRect;
  228.     r.top   = 135;
  229.     r.right =  20;
  230.     EraseRect(&r);
  231.     if (reading) MoveTo(8,150); else MoveTo(8,170);
  232.     TextFace(0);
  233.     DrawString("\p•");
  234.     }
  235.  
  236. static void StUU_Exit(void)
  237.     {
  238.     if (InBuffer )    { DisposeHandle(InBuffer );    InBuffer      = NULL; }
  239.     if (OutBuffer1)   { DisposeHandle(OutBuffer1);   OutBuffer1    = NULL; }
  240.     if (OutBuffer2)   { DisposeHandle(OutBuffer2);   OutBuffer2    = NULL; }
  241.     if (StatusWindow) { DisposeWindow(StatusWindow); StatusWindow  = NULL; }
  242.     if (IC_instance)  { ICStop(IC_instance);         IC_instance   = NULL; }
  243.     }
  244.  
  245. static Boolean TrapAvailable(unsigned long trap)
  246.     {
  247.     TrapType tType = (trap & 0x800 ? ToolTrap : OSTrap);
  248.     if (trap & 0x800)                // if it is a ToolBox Trap
  249.         {
  250.         unsigned long n = 0x400;    // number of toolbox traps
  251.         if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
  252.             n = 0x200;
  253.         if ((trap &= 0x7FF) >= n) trap = _Unimplemented;
  254.         }
  255.     return(NGetTrapAddress(trap, tType) != NGetTrapAddress(_Unimplemented, ToolTrap));
  256.     }
  257.  
  258. #ifdef __MC68K__
  259.  
  260. // The definition of GetHoldableBytes for 68K code
  261.  
  262. #if !GENERATINGCFM
  263. #pragma parameter __D0 GetHoldableBytes()
  264. #endif
  265. extern pascal Size GetHoldableBytes(void) TWOWORDINLINE(0x70FD, 0xA05C);
  266. #else
  267.  
  268. // The definition of GetHoldableBytes for PPC code
  269.  
  270. static pascal Size GetHoldableBytes(void)
  271.     {
  272.     static short GetHoldableBytes68K[] =
  273.         {
  274.         0x70FD,        // moveq #-3,d0
  275.         0xA05C,        // MemoryDispatch
  276.         0x4E75,        // rts
  277.         };
  278.     enum
  279.         {
  280.         uppGetHoldableBytesProcInfo = kRegisterBased
  281.         | RESULT_SIZE(kFourByteCode) | REGISTER_RESULT_LOCATION(kRegisterD0)
  282.         };
  283.     return(CallUniversalProc((UniversalProcPtr)GetHoldableBytes68K, uppGetHoldableBytesProcInfo));
  284.     }
  285.  
  286. #endif
  287.  
  288. static Size sensibleTempMaxMem(Size *grow)
  289.     {
  290.     Size freemem = TempMaxMem(grow);
  291.     long VMAttr, PhysicalRAM, LogicalRAM;
  292.     
  293.     // if VM on, see if we can revise our estimate of free memory to a more sensible value
  294.     if (TrapAvailable(_Gestalt) &&
  295.         Gestalt(gestaltVMAttr, &VMAttr) == noErr && (VMAttr & (1 << gestaltVMPresent)) &&
  296.         Gestalt(gestaltPhysicalRAMSize, &PhysicalRAM) == noErr &&
  297.         Gestalt(gestaltLogicalRAMSize, &LogicalRAM) == noErr)
  298.         {
  299.         // See how much physical RAM is available
  300.         // If MemoryDispatch is available, use it, else just use
  301.         // a simplistic estimate that half of the physical RAM is holdable
  302.         Size holdable = TrapAvailable(_MemoryDispatch) ? GetHoldableBytes() : PhysicalRAM / 2;
  303.         // See how much memory is in use
  304.         Size usedmem = LogicalRAM - TempFreeMem();
  305.         // See how much physical RAM is free
  306.         Size useable = 0;
  307.         if (holdable > usedmem) useable = holdable - usedmem;
  308.         // If there is very little (or no) physical RAM left, then we'll
  309.         // steal 1/16 of the holdable pages (e.g. 1M on a 16M machine)
  310.         if (useable < holdable/16) useable = holdable/16;
  311.         // If our initial freemem value is more than this amount
  312.         // that we deem sensible to allocate, reduce it 
  313.         if (freemem > useable) freemem = useable;
  314.         }
  315.     return(freemem);
  316.     }
  317.  
  318. #pragma segment Main
  319. pascal Boolean PreFlightDocs (Boolean opening, short itemCount, Handle *userDataHandle) {
  320. #pragma unused ( itemCount )
  321. #pragma unused ( userDataHandle )
  322.     OSErr resultCode1 = noErr, resultCode2 = noErr;
  323.     
  324.     ICError ICerr;
  325.     ICAttr attr;
  326.     Size maxTM, maxHM, growTM = 0, growHM = 0;
  327.     
  328.     if (!opening) return(FALSE);    // we support opening, but not printing - see above
  329.     
  330.     MakeStatusWindow();
  331.     ICerr = ICStart(&IC_instance, 'StUU');
  332.     if (!ICerr) ICerr = ICFindConfigFile(IC_instance, 0, nil);
  333.     if (!ICerr) ICerr = ICGetPrefHandle(IC_instance, kICMapping, &attr, &IC_mappings);
  334.     if (ICerr) { (void)Alert(Alert_NoIC, NULL); IC_mappings = NULL; }
  335.  
  336.     // See how much free memory we have in the temporary pool, and in the application heap
  337.     maxTM = sensibleTempMaxMem(&growTM) & ~0xFFF;
  338.     maxHM = MaxMem(&growHM)     & ~0xFFF;
  339.  
  340.     // Make sure we keep 32K of application memory in reserve for alerts etc.
  341.     if (maxHM < 0x8000) maxHM = 0; else maxHM -= 0x8000;
  342.  
  343.     // Note: We lock all the handles as soon as possible, otherwise the System has
  344.     // a habit of trying to copy them around to compact the heap, and that absolutely
  345.     // kills performance. If we don't lock the handles, on a machine with VM the
  346.     // launch time can be 10-20 seconds, even using the sensibleTempMaxMem routine.
  347.     if (maxTM > maxHM)
  348.         {
  349.         InHandleSize = maxTM >> 1;
  350.         OutHandleSize = InHandleSize >> 1;
  351.         while (InHandleSize > 0x1000)
  352.             {
  353.             InBuffer  = TempNewHandle(InHandleSize, &resultCode1);
  354.             if (resultCode1) { InHandleSize >>= 1; continue; }
  355.             HLock(InBuffer);
  356.             break;
  357.             }
  358.         while (OutHandleSize > 0x1000)
  359.             {
  360.             OutBuffer1 = TempNewHandle(OutHandleSize, &resultCode2);
  361.             if (resultCode2) { OutHandleSize >>= 1; continue; }
  362.             HLock(OutBuffer1);
  363.             OutBuffer2 = TempNewHandle(OutHandleSize, &resultCode2);
  364.             if (resultCode2) { DisposeHandle(OutBuffer1); OutBuffer1 = NULL; OutHandleSize >>= 1; continue; }
  365.             HLock(OutBuffer2);
  366.             break;
  367.             }
  368.         }
  369.     else
  370.         {
  371.         InHandleSize = maxHM >> 1;
  372.         OutHandleSize = InHandleSize >> 1;
  373.         while (InHandleSize > 0x1000)
  374.             {
  375.             InBuffer  = NewHandle(InHandleSize);
  376.             if (!InBuffer) { InHandleSize >>= 1; continue; }
  377.             HLock(InBuffer);
  378.             break;
  379.             }
  380.         while (OutHandleSize > 0x1000)
  381.             {
  382.             OutBuffer1 = NewHandle(OutHandleSize);
  383.             if (!OutBuffer1) { InHandleSize >>= 1; continue; }
  384.             HLock(OutBuffer1);
  385.             OutBuffer2 = NewHandle(OutHandleSize);
  386.             if (!OutBuffer2) { DisposeHandle(OutBuffer1); OutBuffer1 = NULL; OutHandleSize >>= 1; continue; }
  387.             HLock(OutBuffer2);
  388.             break;
  389.             }
  390.         resultCode1 = (InBuffer   == NULL);
  391.         resultCode2 = (OutBuffer2 == NULL);
  392.         }
  393.     if (resultCode1 || resultCode2) { (void)Alert(Alert_NoMemory, NULL); StUU_Exit(); return(FALSE); }
  394.  
  395.     OutBuffer = OutBuffer1;
  396.     ShowWindow(StatusWindow);
  397.     PaintStatusWindow();
  398.     
  399. #if DO_TIMING
  400.     Microseconds(&total_start_time);
  401.     create_time.lo = create_time.hi = 0;
  402.     write_time .lo = write_time .hi = 0;
  403.     close_time .lo = close_time .hi = 0;
  404.     read_time  .lo = read_time  .hi = 0;
  405. #endif
  406.     return(TRUE);
  407. }
  408.  
  409.  
  410. /*    
  411.     This routine is called for each file passed in the ODOC event.
  412.     
  413.     In this routine you would place code for processing each file/folder/disk that
  414.     was dropped on top of you.
  415.     
  416.     You will probably want to remove the #pragma unused (currently there to fool the compiler!)
  417. */
  418. #pragma segment Main
  419. pascal void OpenDoc ( FSSpecPtr myFSSPtr, Boolean opening, Handle userDataHandle ) {
  420. #pragma unused ( myFSSPtr )
  421. #pragma unused ( opening )
  422. #pragma unused ( userDataHandle )
  423.     OSErr    err = noErr;
  424.     
  425.     
  426.     #ifdef qWalkFolders
  427.     /*
  428.         For this case we need to determine if the FSSpec is a file or folder.
  429.         If it's a folder, we then need to process each item in that folder,
  430.         otherwise just process the item.
  431.     */
  432.     if (FSpIsFolder(myFSSPtr))
  433.         err = ProcessFolder(myFSSPtr);
  434.     else
  435.         err = ProcessItem(myFSSPtr);
  436.     #else
  437.     /*
  438.         For this case we just call ProcessItem on the FSSpec above.
  439.     */
  440.     err = ProcessItem(myFSSPtr);
  441.     #endif
  442.     
  443.     // you should probably do something if you get back an error ;)
  444. }
  445.  
  446. // CopyData Benchmarks
  447. //  82 182 number of bytes (copied 100,000 times)
  448. // --- ---
  449. // 145 212 BlockMove
  450. // 145 212 BlockMoveData
  451. // 134 283 CopyData
  452. // 113 237 CopyData in assembler
  453. // Conclusion: For small blocks, a subroutine is quicker than the BlockMove trap
  454.  
  455. static void CopyData(register Ptr src, register Ptr dest, register unsigned short len)
  456.     {
  457. #if THINK_C
  458.     asm    {
  459.         sub.w    #1, len
  460.     @0    move.b    (src)+, (dest)+
  461.         dbra    len, @0
  462.         }
  463. #else
  464.     while(len) { *dest++ = *src++; len--; }
  465. #endif
  466.     }
  467.  
  468. static void ShowMsg(unsigned char *msg, unsigned long time)
  469.     {
  470.     unsigned char buffer[128], number[16];
  471.     NumToString(time, number);
  472.     CopyData((Ptr)msg    + 1, (Ptr)buffer + 1, msg[0]);
  473.     CopyData((Ptr)number + 1, (Ptr)buffer + 1 + msg[0], number[0]);
  474.     buffer[0] = msg[0] + number[0];
  475.     DebugStr(buffer);
  476.     }
  477.  
  478. static void ShowWindowMsg(short y, unsigned char *msg, unsigned long time)
  479.     {
  480.     unsigned char buffer[128], number[16];
  481.     NumToString(time, number);
  482.     CopyData((Ptr)msg    + 1, (Ptr)buffer + 1, msg[0]);
  483.     CopyData((Ptr)number + 1, (Ptr)buffer + 1 + msg[0], number[0]);
  484.     buffer[0] = msg[0] + number[0];
  485.     MoveTo(20, y); DrawString(buffer);
  486.     }
  487.  
  488.  
  489. /*    
  490.     This routine is the last routine called as part of an ODOC event.
  491.     
  492.     In this routine you would place code to process any structures, etc. 
  493.     that you setup in the PreflightDocs routine.
  494.  
  495.     NEW IN 2.0!
  496.     The itemCount parameter was the number of items that you processed.
  497.     It is passed here just in case you need it ;)  
  498.     
  499.     If you created a userDataHandle in the PreFlightDocs routines, this is
  500.     the place to dispose of it since the Shell will NOT do it for you!
  501.     
  502.     You will probably want to remove the #pragma unusued (currently there to fool the compiler!)
  503. */
  504.  
  505. #pragma segment Main
  506. pascal void PostFlightDocs ( Boolean opening, short itemCount, Handle userDataHandle ) {
  507. #pragma unused ( opening )
  508. #pragma unused ( itemCount )
  509. #pragma unused ( userDataHandle )
  510.  
  511.     if ( (opening) && (!gOApped) )
  512.         gDone = true;    //    close everything up!
  513.  
  514.     HUnlock(InBuffer);
  515.     HUnlock(OutBuffer1);
  516.     HUnlock(OutBuffer2);
  517.  
  518. #if DO_TIMING
  519.     Microseconds(&total_end_time);
  520.     WideSubtract((wide*)&total_end_time, (wide*)&total_start_time);
  521.     EraseRect(&StatusWindow->portRect);
  522.     ShowWindowMsg( 30, "\pRead: ",   read_time.hi     *1000 + read_time.lo/1000);
  523.     ShowWindowMsg( 50, "\pCreate: ", create_time.hi   *1000 + create_time.lo/1000);
  524.     ShowWindowMsg( 70, "\pWrite: ",  write_time.hi    *1000 + write_time.lo/1000);
  525.     ShowWindowMsg( 90, "\pClose: ",  close_time.hi    *1000 + close_time.lo/1000);
  526.     ShowWindowMsg(110, "\pTotal: ",  total_end_time.hi*1000 + total_end_time.lo/1000);
  527.     while (!Button()) continue;
  528. #endif
  529.  
  530.     StUU_Exit();
  531.     
  532.     /*
  533.         The reason we do not auto quit is based on a recommendation in the
  534.         Apple event Registry which specifically states that you should NOT
  535.         quit on a 'pdoc' as the Finder will send you a 'quit' when it is 
  536.         ready for you to do so.
  537.     */
  538. }
  539.  
  540. // *********************************************************************************
  541. // Main StUU Code body starts here
  542.  
  543. // I treat any non-printing ascii character as a line break (non-printing ascii
  544. // characters except for CR and/or NL have no business being in UUencoded data anyway)
  545. #define IS_LINE_BREAK(C) ((unsigned char)(C) < (unsigned char)32)
  546.  
  547. static Ptr ReadNextLine(void)
  548.     {
  549.     register Ptr theData;
  550.     register Ptr InPtrCopy  = InPtr;
  551.     register Ptr InPtrLimit = InPtrCopy + 255;
  552.     
  553.     // If data is all contiguous in memory use fast optimistic case
  554.     if (InPtrLimit < InEnd)
  555.         {
  556.         if (IS_LINE_BREAK(*InPtrCopy)) InPtrCopy++;
  557.         theData = InPtrCopy;
  558.         while (InPtrCopy < InPtrLimit && !IS_LINE_BREAK(*InPtrCopy)) InPtrCopy++;
  559.         if (InPtrCopy > theData)                    // If we read a non-blank line
  560.             {
  561.             LineLength = InPtrCopy - theData;        // Set the length
  562.             *InPtrCopy++ = 0;                        // Terminate the line
  563.             InPtr = InPtrCopy;                        // Update the pointer
  564.             return(theData);                        // and return
  565.             }
  566.         }
  567.  
  568.     // If the optimistic case fails, try the slow mechanism
  569.     InPtrLimit = InEnd;
  570.     theData = TempLineBuffer;
  571.  
  572.     while (1)
  573.         {
  574.         while (InPtrCopy < InPtrLimit && theData < &TempLineBuffer[255] &&
  575.             !IS_LINE_BREAK(*InPtrCopy)) *theData++ = *InPtrCopy++;
  576.         
  577.         if (InPtrCopy < InPtrLimit)        // We haven't run out of data yet
  578.             {
  579.             InPtrCopy++;    // Skip over the newline
  580.             // Test to see if the line is has some contents (empty lines are ignored)
  581.             if (theData > TempLineBuffer) break;
  582.             }
  583.         else    // We broke out of loop because this chunk is over, so read some more
  584.             {
  585.             OSErr err;
  586.             long inOutCount = BytesRemaining;
  587.             if (inOutCount > InHandleSize) inOutCount = InHandleSize;
  588.             if (inOutCount == 0) break;        // If no more to read, stop
  589. #if DO_TIMING
  590.             Microseconds(&read_start_time);
  591. #endif
  592.             UpdateStatusWindow(TRUE);
  593.             err = FSRead(ReadRefNum, &inOutCount, *InBuffer);
  594.             UpdateStatusWindow(FALSE);
  595. #if DO_TIMING
  596.             Microseconds(&read_end_time);
  597.             WideSubtract((wide*)&read_end_time, (wide*)&read_start_time);
  598.             WideAdd((wide*)&read_time, (wide*)&read_end_time);
  599. #endif
  600.             if (err) break;                    // If error reading, stop
  601.             InPtrCopy = *InBuffer;
  602.             InPtrLimit = InPtrCopy + inOutCount;
  603.             BytesRemaining -= inOutCount;
  604.             if (inOutCount == 0) { DebugStr("\pError: FSRead read nothing"); break; }
  605.             }
  606.         }
  607.  
  608.     LineLength = theData - TempLineBuffer;    // Set the length
  609.     *theData = 0;                            // Terminate the line
  610.     InPtr = InPtrCopy;                        // Update the pointers
  611.     InEnd = InPtrLimit;
  612.     return(TempLineBuffer);
  613.     }
  614.  
  615. static void GetFileType(const unsigned char *name, OSType *t, OSType *c)
  616.     {
  617.     ICMapEntry entry;
  618.     ICError ICerr = -1;
  619.     *t = *c = '????';
  620.     if (IC_mappings) ICerr = ICMapEntriesFilename(IC_instance, IC_mappings, name, &entry);
  621.     if (ICerr == noErr) { *t = entry.file_type; *c = entry.file_creator; }
  622.     }
  623.  
  624. static void SetNextName(void)
  625.     {
  626.     outputSpec.name[output_namesuffix_length  ] = output_namesuffix++;
  627.     outputSpec.name[0] = output_namesuffix_length;
  628.     outputSpec.name[output_namesuffix_length-1] = '.';
  629.     if (output_namesuffix == '9'+1) output_namesuffix = 'A';
  630.     }
  631.  
  632. static pascal void CreateComplete(void)
  633.     {
  634. #ifdef __MC68K__
  635.     ex_HParamBlockRec *pb = GetRegisterA0();
  636.     void *oldGlobals = GetGlobalsRegister();
  637.     SetGlobalsRegister(pb->GlobalsReg);
  638. #endif
  639.     if (Async_Create_PB.io.ioParam.ioResult == dupFNErr)            // Duplicate name; try again
  640.         { SetNextName(); PBHCreateAsync(&Async_Create_PB.io); }
  641.     else FileBeingCreated = FALSE;
  642. #ifdef __MC68K__
  643.     SetGlobalsRegister(oldGlobals);
  644. #endif
  645.     }
  646.  
  647. #if GENERATINGCFM
  648. static RoutineDescriptor CreateCompleteRD =  BUILD_ROUTINE_DESCRIPTOR(uppIOCompletionProcInfo, CreateComplete);
  649. #else
  650. #define CreateCompleteRD CreateComplete
  651. #endif
  652.  
  653. static OSErr StUU_Create(FSSpecPtr sourceFSSPtr, unsigned char *fileName)
  654.     {
  655.     OSErr err = noErr;
  656.     unsigned long the_time;
  657.  
  658. #if DO_TIMING
  659.     Microseconds(&create_start_time);
  660. #endif
  661.  
  662.     // Set up new FSSpec
  663.     outputSpec.vRefNum = sourceFSSPtr->vRefNum;
  664.     outputSpec.parID   = sourceFSSPtr->parID;
  665.     CopyData((Ptr)fileName, (Ptr)outputSpec.name, 1+fileName[0]);
  666.  
  667.     // Prepare the name suffix, in case we need it because of duplicate file names
  668.     output_namesuffix = '1';
  669.     output_namesuffix_length = fileName[0]+2;
  670.     if (output_namesuffix_length > 31) output_namesuffix_length = 31;
  671.  
  672.     // Fire off the asynchronous file creation
  673.     FileBeingCreated = TRUE;
  674.     Async_Create_PB.io.ioParam.ioCompletion = &CreateCompleteRD;
  675.     Async_Create_PB.io.ioParam.ioResult     = noErr;
  676.     Async_Create_PB.io.ioParam.ioNamePtr    = outputSpec.name;
  677.     Async_Create_PB.io.ioParam.ioVRefNum    = outputSpec.vRefNum;
  678.     Async_Create_PB.io.fileParam.ioDirID    = outputSpec.parID;
  679. #ifdef __MC68K__
  680.     Async_Create_PB.GlobalsReg = GetGlobalsRegister();
  681. #endif
  682.     err = PBHCreateAsync(&Async_Create_PB.io);
  683.     if (err) FileBeingCreated = FALSE;
  684.  
  685.     // Ask Internet Config to tell us the type and creator codes for this file        
  686.     GetFileType(fileName, &output_type, &output_creator);
  687.  
  688.     // Wait for file creation to complete
  689.     while (FileBeingCreated) continue;
  690.     if (Async_Create_PB.io.ioParam.ioResult) err = Async_Create_PB.io.ioParam.ioResult;
  691.  
  692.     if (!err)
  693.         {
  694.         // Set the creator and type
  695.         GetDateTime(&the_time);
  696.         Async_Info_PB.fileParam.ioCompletion = NULL;
  697.         Async_Info_PB.fileParam.ioNamePtr    = outputSpec.name;
  698.         Async_Info_PB.fileParam.ioVRefNum    = outputSpec.vRefNum;
  699.         Async_Info_PB.fileParam.ioDirID      = outputSpec.parID;
  700.         Async_Info_PB.fileParam.ioFlCrDat    = the_time;
  701.         Async_Info_PB.fileParam.ioFlMdDat    = the_time;
  702.         Async_Info_PB.fileParam.ioFlFndrInfo.fdType     = output_type;
  703.         Async_Info_PB.fileParam.ioFlFndrInfo.fdCreator  = output_creator;
  704.         Async_Info_PB.fileParam.ioFlFndrInfo.fdFlags    = 0;
  705.         Async_Info_PB.fileParam.ioFlFndrInfo.fdLocation = zeroPoint;
  706.         Async_Info_PB.fileParam.ioFlFndrInfo.fdFldr     = 0;
  707.         err = PBHSetFInfoAsync(&Async_Info_PB);
  708.         }
  709.     
  710.     if (!err)
  711.         {
  712.         // Open the file
  713.         Async_Open_PB.fileParam.ioCompletion = NULL;
  714.         Async_Open_PB.fileParam.ioNamePtr    = outputSpec.name;
  715.         Async_Open_PB.fileParam.ioVRefNum    = outputSpec.vRefNum;
  716.         Async_Open_PB.fileParam.ioDirID      = outputSpec.parID;
  717.         Async_Open_PB.ioParam.ioPermssn      = fsWrPerm;
  718.         err = PBHOpenDFAsync(&Async_Open_PB);
  719.         }
  720.  
  721. #if DO_TIMING
  722.     Microseconds(&create_end_time);
  723.     WideSubtract((wide*)&create_end_time, (wide*)&create_start_time);
  724.     WideAdd((wide*)&create_time, (wide*)&create_end_time);
  725. #endif
  726.  
  727.     if (err) { FSpDelete(&outputSpec); ErrorAlert(kErrStringID, kCantCreateErr, err); }
  728.     else UpdateStatusWindowOutput(outputSpec.name);
  729.  
  730.     return(err);
  731.     }
  732.  
  733. static void ReportErrorAndDelete(void)
  734.     {
  735.     GrafPtr savePort;
  736.     GetPort(&savePort);
  737.     FSpDelete(&closeSpec);
  738.     ParamText(closeSpec.name, NULL, NULL, NULL);
  739.     (void)Alert(Alert_DiskError, NULL);
  740.     SetPort(savePort);
  741.     }
  742.  
  743. static OSErr WriteChunk(void)
  744.     {
  745.     OSErr err;
  746. #if DO_TIMING
  747.     Microseconds(&write_start_time);
  748. #endif
  749.     while (Async_Open_PB.ioParam.ioResult == 1) continue; // Make sure open is complete
  750.     while (Async_Write_PB.ioParam.ioResult == 1) continue; // Make sure previous write is complete
  751.     
  752.     // If the previous write failed, report it now
  753.     if (Async_Write_PB.ioParam.ioResult) return(Async_Write_PB.ioParam.ioResult);
  754.     
  755.     // If our file opened correctly, write to it
  756.     if (Async_Write_PB.ioParam.ioResult == noErr && Async_Open_PB.ioParam.ioResult == noErr)
  757.         {
  758.         writeSpec = outputSpec;
  759.         Async_Write_PB.ioParam.ioCompletion = NULL;
  760.         Async_Write_PB.ioParam.ioResult     = noErr;
  761.         Async_Write_PB.ioParam.ioRefNum     = Async_Open_PB.ioParam.ioRefNum;
  762.         Async_Write_PB.ioParam.ioBuffer     = *OutBuffer;
  763.         Async_Write_PB.ioParam.ioReqCount   = OutPtr - *OutBuffer;
  764.         Async_Write_PB.ioParam.ioPosMode    = fsAtMark;
  765.         Async_Write_PB.ioParam.ioPosOffset  = 0;
  766.         err = PBWriteAsync(&Async_Write_PB);
  767.         if (err) { Async_Write_PB.ioParam.ioResult = err; err = noErr; }
  768.         if (OutBuffer == OutBuffer1) OutBuffer = OutBuffer2; else OutBuffer = OutBuffer1;
  769.         }
  770.     OutPtr = *OutBuffer;
  771.     OutEnd = *OutBuffer + OutHandleSize;
  772. #if DO_TIMING
  773.         Microseconds(&write_end_time);
  774.         WideSubtract((wide*)&write_end_time, (wide*)&write_start_time);
  775.         WideAdd((wide*)&write_time, (wide*)&write_end_time);
  776. #endif
  777.     return(err);
  778.     }
  779.  
  780. // Note, made the decode table unsigned instead of signed to prevent the C compiler
  781. // from trying to sign-extend the bytes every time it ORs them into a long
  782. static unsigned char zero_char;
  783. static unsigned char decode_table[256];
  784. #define    DEC(c)     (decode_table[(unsigned char)(c)])    /* single character decode */
  785.  
  786. static void reset_default_table(void)
  787.     {
  788.     int i;
  789.     for (i= 0; i< 32; i++) decode_table[i] = 0;
  790.     for (i=32; i< 96; i++) decode_table[i] = i - 32;
  791.     for (i=96; i<256; i++) decode_table[i] = 0;
  792.     decode_table['`'] = decode_table[' '];
  793.     decode_table['~'] = decode_table['^'];
  794.     zero_char = ' ';
  795.     }
  796.  
  797. static void read_translation_table(void)
  798.     {
  799.     unsigned char val = 0;
  800.     int i;
  801.     for (i=0; i<256; i++) decode_table[i] = 0;
  802.     while (CurrentLine[0])
  803.         {
  804.         unsigned char *p;
  805.         CurrentLine = ReadNextLine();
  806.         p = (unsigned char *)CurrentLine;
  807.         while (p[LineLength - 1] == ' ') p[--LineLength] = 0;
  808.         if (val == 0) zero_char = *p;
  809.         while (*p) decode_table[*p++] = val++;
  810.         if (val >= 64) return;
  811.         }
  812.     }
  813.  
  814. /* Old Think C inline assembly chunk
  815. #if THINK_C
  816.         asm {
  817.             clr.l    d0
  818.             move.b    (p)+, d0            ; read first byte
  819.             move.b    (decoder,d0), val    ; move translated value into val
  820.             move.b    (p)+, d0            ; read second byte
  821.             lsl.l    #6, val                ; shift val left six bits
  822.             or.b    (decoder,d0), val    ; OR translated value into val
  823.             move.b    (p)+, d0            ; read third byte
  824.             lsl.l    #6, val                ; shift val left six bits
  825.             or.b    (decoder,d0), val    ; OR translated value into val
  826.             move.b    (p)+, d0            ; read final byte
  827.             lsl.l    #6, val                ; shift val left six bits
  828.             or.b    (decoder,d0), val    ; OR translated value into val
  829.             }
  830. */
  831.  
  832. static OSErr DecodeLine(register Ptr p)
  833.     {
  834.     register Ptr out;
  835.     register unsigned char *decoder = decode_table;
  836.     register unsigned long val;
  837.     register int linelen = decoder[(unsigned char)(*p++)];
  838.     if (OutEnd - OutPtr < 256) { OSErr err = WriteChunk(); if (err) return(err); }
  839.     out = OutPtr;        // Careful! Don't set this till *after* WriteChunk
  840.  
  841.     while (linelen > 0)
  842.         {
  843.         val  = decoder[(unsigned char)(p[0])]; val <<= 6;
  844.         val |= decoder[(unsigned char)(p[1])]; val <<= 6;
  845.         val |= decoder[(unsigned char)(p[2])]; val <<= 6;
  846.         val |= decoder[(unsigned char)(p[3])];
  847.         out[2] = val; val >>= 8;
  848.         out[1] = val; val >>= 8;
  849.         out[0] = val;
  850.         p += 4;
  851.         out += 3;
  852.         linelen -= 3;
  853.         }
  854.     // Because we output blocks of three, we may overshoot the correct end of line
  855.     // In this case linelen will be negative and we backtrack OutPtr by that amount.
  856.     OutPtr = out + linelen;
  857.     return(noErr);
  858.     }
  859.  
  860. #define MAX_STASHED_LINES 8
  861. #define MAX_STASHED_LINE_LENGTH 88
  862. typedef struct { char c[MAX_STASHED_LINE_LENGTH]; } TextLine;
  863.  
  864. #define PERFECT_LINE (CurrentLine[0] == 'M' && LineLength >= 61 && LineLength <= 63)
  865.  
  866. // sourceFSSPtr is the uuencoded file being decoded
  867. // hp points to the "begin 644 filename.foo" header line
  868. static OSErr DecodeFile(FSSpecPtr sourceFSSPtr, Ptr hp)
  869.     {
  870.     OSErr err, closeErr;
  871.     TextLine LastPerfectLine;
  872.     TextLine LineStash[MAX_STASHED_LINES];
  873.     int num_stashed_lines = 0;
  874.     Boolean GotPerfectLine = FALSE;
  875.     int i;
  876.     Ptr filemode;
  877.     unsigned char filename[32];
  878.     filename[0] = 0;
  879.     hp += 6;                            // Skip the "begin "
  880.     while (*hp == ' ') hp++;            // Skip the spaces
  881.     filemode = hp;                        // The Unix file permissions
  882.     while (*hp && *hp != ' ') hp++;        // Read the permission portion
  883.     if (*hp) *hp++ = 0;                    // Terminate the permission string
  884.     while (*hp == ' ') hp++;            // Skip the spaces
  885.     while (*hp && *hp != ' ' && filename[0] < sizeof(filename) - 1)
  886.         filename[++filename[0]] = (unsigned char) *hp++;
  887.  
  888.     CurrentLine = ReadNextLine();        // Read the first line of encoded data
  889.     if (!filename[0]) { ErrorAlert(kErrStringID, kNoFileNameErr, 0); return(noErr); }
  890.  
  891.     // Create output file in the same directory
  892.     err = StUU_Create(sourceFSSPtr, filename);
  893.     if (err) return(err);
  894.     // The file is opened asynchronously, so the open proceeds while we start decoding
  895.     
  896.     OutPtr = *OutBuffer;                // Set up output pointers
  897.     OutEnd = *OutBuffer + OutHandleSize;
  898.     
  899.     while (CurrentLine[0] && !err)
  900.         {
  901.         // "Good Loop": Read as many good lines as possible
  902.         while (PERFECT_LINE && !err)
  903.             { err = DecodeLine(CurrentLine); CurrentLine = ReadNextLine(); }
  904.         
  905.         GotPerfectLine = FALSE;
  906.         // "Bad Loop": Skip past the bad or questionable lines
  907.         while (CurrentLine[0] && !err)
  908.             {
  909.             int origlen = DEC(CurrentLine[0]);                // Original length
  910.             int codedlength = 1 + (origlen * 4 + 2) / 3;    // UUencoded length
  911.             
  912.             if (CurrentLine[0] == 'e' &&        // check for "end" marker
  913.                 CurrentLine[1] == 'n' &&
  914.                 CurrentLine[2] == 'd') goto end_of_file;
  915.             
  916.             if (CurrentLine[0] == 'b' &&        // check for another "begin" marker
  917.                 CurrentLine[1] == 'e' &&
  918.                 CurrentLine[2] == 'g' &&
  919.                 CurrentLine[3] == 'i' &&
  920.                 CurrentLine[4] == 'n' &&
  921.                 CurrentLine[5] == ' ') goto end_of_file;
  922.             
  923.             if (CurrentLine[0] == 't' &&        // check for another "table" marker
  924.                 CurrentLine[1] == 'a' &&
  925.                 CurrentLine[2] == 'b' &&
  926.                 CurrentLine[3] == 'l' &&
  927.                 CurrentLine[4] == 'e') goto end_of_file;
  928.  
  929.             // If line too short, restore truncated trailing spaces
  930.             if (LineLength < codedlength)
  931.                 {
  932.                 // Mustn't try to expand line in place!
  933.                 if (CurrentLine != TempLineBuffer)
  934.                     {
  935.                     CopyData(CurrentLine, TempLineBuffer, LineLength+1);
  936.                     CurrentLine = TempLineBuffer;
  937.                     }
  938.                 while (LineLength < codedlength) CurrentLine[LineLength++] = zero_char;
  939.                 CurrentLine[LineLength] = 0;        // Terminate the line
  940.                 }
  941.             
  942.             // If we get one perfect line stash it, and set the flag.
  943.             // If we get a second perfect line, assume we are back in body text,
  944.             // and go back to the good loop
  945.             if (PERFECT_LINE)
  946.                 {
  947.                 if (GotPerfectLine) { err = DecodeLine(LastPerfectLine.c); break; }
  948.                 num_stashed_lines = 0;
  949.                 GotPerfectLine = TRUE;
  950.                 CopyData(CurrentLine, LastPerfectLine.c, LineLength);
  951.                 }
  952.             else
  953.                 {
  954.                 // Stash runt lines in case they turn out to be
  955.                 // the last few lines before the "end" marker
  956.                 GotPerfectLine = FALSE;
  957.                 if (LineLength <= MAX_STASHED_LINE_LENGTH &&
  958.                     num_stashed_lines < MAX_STASHED_LINES &&
  959.                     origlen > 0 && origlen <= 45 && LineLength <= codedlength + 3)
  960.                         CopyData(CurrentLine, LineStash[num_stashed_lines++].c, LineLength);
  961.                 }
  962.             CurrentLine = ReadNextLine();                        // Read the next line
  963.             }
  964.         }
  965. end_of_file:
  966.     // Decode any stashed lines
  967.     if (!err && GotPerfectLine) err = DecodeLine(LastPerfectLine.c);
  968.     for (i=0; !err && i<num_stashed_lines; i++) err = DecodeLine(LineStash[i].c);
  969.  
  970.     // Make sure open is complete
  971.     while (Async_Open_PB.ioParam.ioResult == 1) continue;
  972.     
  973.     // If file failed to be opened, give the error message
  974.     if (Async_Open_PB.ioParam.ioResult)
  975.         {
  976.         FSpDelete(&outputSpec);
  977.         ErrorAlert(kErrStringID, kCantCreateErr, Async_Open_PB.ioParam.ioResult);
  978.         return(Async_Open_PB.ioParam.ioResult);
  979.         }
  980.  
  981.     // Write the data out to disk
  982.     if (!err && OutPtr > *OutBuffer) err = WriteChunk();
  983.     // The file is written asynchronously, so the write proceeds while we continue
  984.     // decoding the next file
  985.  
  986.     // And close the file
  987. #if DO_TIMING
  988.     Microseconds(&close_start_time);
  989. #endif
  990.     while (Async_Close_PB.ioParam.ioResult == 1) continue; // Make sure previous close is complete
  991.     if (Async_Close_PB.ioParam.ioResult) ReportErrorAndDelete();
  992.     // If the previous close failed, report it now
  993.  
  994.     closeSpec = outputSpec;
  995.     Async_Close_PB.ioParam.ioCompletion = NULL;
  996.     Async_Close_PB.ioParam.ioResult     = noErr;
  997.     Async_Close_PB.ioParam.ioRefNum     = Async_Open_PB.ioParam.ioRefNum;
  998.     closeErr = PBCloseAsync(&Async_Close_PB);
  999.     if (closeErr) Async_Close_PB.ioParam.ioResult = closeErr;
  1000.     // The file is closed asynchronously, so the close proceeds while we continue
  1001.     // decoding the next file
  1002.  
  1003. #if DO_TIMING
  1004.     Microseconds(&close_end_time);
  1005.     WideSubtract((wide*)&close_end_time, (wide*)&close_start_time);
  1006.     WideAdd((wide*)&close_time, (wide*)&close_end_time);
  1007. #endif
  1008.     
  1009.     if (err) ReportErrorAndDelete();
  1010.  
  1011.     return(err);
  1012.     }
  1013.  
  1014. // *********************************************************************************
  1015.  
  1016. /*
  1017.     This routine gets called for each item (which could be either a file or a folder)
  1018.     that the caller wants dropped.  The determining factor is the definition of the 
  1019.     qWalkFolder compiler directive.   Either way, the item in question should be
  1020.     processed as a single item and not "dissected" into component units (like subfiles
  1021.     of a folder!)
  1022. */
  1023. static OSErr ProcessItem(FSSpecPtr myFSSPtr)
  1024. {
  1025.     OSErr    err = noErr;
  1026.     
  1027.     err = FSpOpenDF(myFSSPtr, fsRdPerm, &ReadRefNum);
  1028.     if (err) return(err);
  1029.     
  1030.     err = GetEOF(ReadRefNum, &BytesRemaining);
  1031.     if (err) { FSClose(ReadRefNum); return(err); }
  1032.     InPtr = InEnd = NULL;
  1033.     
  1034.     UpdateStatusWindowInput(myFSSPtr->name);
  1035.     
  1036.     reset_default_table();
  1037.     CurrentLine = ReadNextLine();        // Load first line of file into CurrentLine
  1038.     while (err == noErr && CurrentLine[0])
  1039.         {
  1040.         if (CurrentLine[0] == 'e' &&        // check for "end" marker
  1041.             CurrentLine[1] == 'n' &&
  1042.             CurrentLine[2] == 'd') reset_default_table();
  1043.         
  1044.         if (CurrentLine[0] == 't' &&
  1045.             CurrentLine[1] == 'a' &&
  1046.             CurrentLine[2] == 'b' &&
  1047.             CurrentLine[3] == 'l' &&
  1048.             CurrentLine[4] == 'e') read_translation_table();
  1049.             
  1050.         if (CurrentLine[0] == 'b' &&
  1051.             CurrentLine[1] == 'e' &&
  1052.             CurrentLine[2] == 'g' &&
  1053.             CurrentLine[3] == 'i' &&
  1054.             CurrentLine[4] == 'n' &&
  1055.             CurrentLine[5] == ' ')
  1056.                 {
  1057.                 err = DecodeFile(myFSSPtr, CurrentLine);
  1058.                 if (IsError(Async_Write_PB.ioParam.ioResult)) break;
  1059.                 reset_default_table();
  1060.                 }
  1061.         else CurrentLine = ReadNextLine();    // Read next line
  1062.         }
  1063.     FSClose(ReadRefNum);
  1064.     while (Async_Close_PB.ioParam.ioResult == 1) continue; // Make sure final output close is complete
  1065.  
  1066.     // If we exited because of a write error, report it and delete the file
  1067.     if (Async_Write_PB.ioParam.ioResult)
  1068.         {
  1069.         ErrorAlert(kErrStringID, kDiskFullErr, Async_Write_PB.ioParam.ioResult);
  1070.         FSpDelete(&writeSpec);
  1071.         ParamText(writeSpec.name, NULL, NULL, NULL);
  1072.         (void)Alert(Alert_DiskError, NULL);
  1073.         }
  1074.     if (Async_Close_PB.ioParam.ioResult) ReportErrorAndDelete();
  1075.     return(err);
  1076. }
  1077.  
  1078. /*
  1079.     This routine gets called for any folder (or disk) that the caller wants 
  1080.     processed as a set of component items, instead of as a single entity.
  1081.     The determining factor is the definition of the qWalkFolder compiler directive.
  1082. */
  1083. static OSErr ProcessFolder(FSSpecPtr myFSSPtr)
  1084. {
  1085.     OSErr        err = noErr;
  1086.     short        index, oldIndex, localIndex;
  1087.     FSSpec        localFSSpec, curFSSpec;
  1088.     CInfoPBRec    cipb;
  1089.     Str255        fName, vFName;
  1090.     long        dirID, origDirID;
  1091.     Boolean        foundPosition;
  1092.  
  1093.      // copy the source locally to avoid recursion problems
  1094.      BlockMoveData(myFSSPtr, &localFSSpec, sizeof(FSSpec));
  1095.      
  1096.     //    get the dirID for THIS folder, not it's parent!
  1097.     BlockMoveData(localFSSpec.name, fName, 32);
  1098.     
  1099.     cipb.hFileInfo.ioCompletion    = 0L;
  1100.     cipb.hFileInfo.ioNamePtr    = fName;
  1101.     cipb.hFileInfo.ioVRefNum    = localFSSpec.vRefNum;
  1102.     cipb.hFileInfo.ioFDirIndex    = 0;    // use the dir & vRefNum;
  1103.     cipb.hFileInfo.ioDirID        = localFSSpec.parID;
  1104.     err = PBGetCatInfoSync(&cipb);
  1105.     
  1106.     if (!err) {        
  1107.         origDirID = cipb.dirInfo.ioDrDirID; // copy the sucker
  1108.         index = 1;
  1109.                 
  1110.         // index through all contents of this folder
  1111.         while (err == noErr) {
  1112.             dirID = origDirID;
  1113.             localIndex = index;
  1114.             fName [0] = 0;
  1115.             cipb.hFileInfo.ioCompletion    = 0L;
  1116.             cipb.hFileInfo.ioNamePtr    = fName;
  1117.             cipb.hFileInfo.ioVRefNum    = localFSSpec.vRefNum;
  1118.             cipb.hFileInfo.ioFDirIndex    = localIndex;    // use a real index
  1119.             cipb.hFileInfo.ioDirID        = dirID;
  1120.             err = PBGetCatInfoSync(&cipb);
  1121.  
  1122.             if (!err) {
  1123.                 BlockMoveData(fName, curFSSpec.name, 32);
  1124.                 curFSSpec.vRefNum    = cipb.hFileInfo.ioVRefNum;
  1125.                 curFSSpec.parID        = dirID;
  1126.             
  1127.                 /*    
  1128.                     Check to see if this entry is a folder.
  1129.                 */
  1130.                 if (cipb.hFileInfo.ioFlAttrib & ioDirMask) {
  1131.                     err = ProcessFolder(&curFSSpec);
  1132.                  } else
  1133.                     err = ProcessItem(&curFSSpec);
  1134.             
  1135.                 /*    If we've had an error, get out! */
  1136.                 if (err)    break;
  1137.  
  1138.                 // dirID = origDirID;    
  1139.                 localIndex = index;    
  1140.  
  1141.                 /*    
  1142.                     Now take into account new files being created
  1143.                     in the current directory & messing up our index.
  1144.                     See Dev.CD Vol. XI:Tools & Apps (Moof!):Misc Utilities:
  1145.                     Disinfectant & Source 2.5.1:Sample:Notes:Scan Alg    
  1146.                 */
  1147.                 vFName [0] = 0;
  1148.                 cipb.hFileInfo.ioCompletion    = 0L;
  1149.                 cipb.hFileInfo.ioNamePtr    = vFName;
  1150.                 cipb.hFileInfo.ioVRefNum    = localFSSpec.vRefNum;
  1151.                 cipb.hFileInfo.ioFDirIndex    = localIndex;    // use a real index
  1152.                 cipb.hFileInfo.ioDirID        = dirID;
  1153.                 err = PBGetCatInfoSync(&cipb);
  1154.                 oldIndex = index;
  1155.                 if (!err) {
  1156.                     /*    If they're equal - same place, go to next */
  1157.                     if (EqualString (vFName, fName, false, false))
  1158.                         index++;
  1159.                 }
  1160.                 
  1161.                 /*    If we didn't advance, then perhaps a file was created or deleted */
  1162.                 if (oldIndex == index) {
  1163.                     oldIndex        = index;    /* save off the old */
  1164.                     index            = 0;        /* and start at the beginning */
  1165.                     err                = noErr;
  1166.                     vFName [0]        = 0;
  1167.                     foundPosition    = false;
  1168.                     
  1169.                     while (!foundPosition) {
  1170.                         index++;
  1171.                         vFName [0] = 0;
  1172.                         cipb.hFileInfo.ioCompletion    = 0L;
  1173.                         cipb.hFileInfo.ioNamePtr    = vFName;
  1174.                         cipb.hFileInfo.ioVRefNum    = localFSSpec.vRefNum;
  1175.                         cipb.hFileInfo.ioFDirIndex    = index;    /* now use a real index */
  1176.                         cipb.hFileInfo.ioDirID        = dirID;
  1177.                         err = PBGetCatInfoSync(&cipb);
  1178.                         
  1179.                         if (err == fnfErr) {  // we've just been deleted
  1180.                             index = oldIndex;
  1181.                             foundPosition = true;
  1182.                             err = noErr;    // have to remember to reset this!
  1183.                         }
  1184.                         
  1185.                     /*    found same file & same index position */
  1186.                     /*    so try the next item */
  1187.                         if ((!foundPosition) && EqualString(fName, vFName, false, false)) {
  1188.                             index++;
  1189.                             foundPosition = true;
  1190.                         }
  1191.                     }
  1192.                 }
  1193.             }
  1194.         }
  1195.     }
  1196.     
  1197.     return(err);
  1198. }
  1199.  
  1200. /*
  1201.     This routine is called when the user chooses "Select File…" from the
  1202.     File Menu.
  1203.     
  1204.     Currently it simply calls the new StandardGetFile routine to have the
  1205.     user select a single file (any type, numTypes = -1) and then calls the
  1206.     SendODOCToSelf routine in order to process it.  
  1207.             
  1208.     The reason we send an odoc to ourselves is two fold: 1) it keeps the code
  1209.     cleaner as all file openings go through the same process, and 2) if events
  1210.     are ever recordable, the right things happen (this is called Factoring!)
  1211.  
  1212.     Modification of this routine to only select certain types of files, selection
  1213.     of multiple files, and/or handling of folder & disk selection is left 
  1214.     as an exercise to the reader.
  1215. */
  1216. pascal void SelectFile(void)
  1217. {
  1218.     StandardFileReply    stdReply;
  1219.     SFTypeList            theTypeList;
  1220.  
  1221.     StandardGetFile(NULL, -1, theTypeList, &stdReply);
  1222.     if (stdReply.sfGood)    // user did not cancel
  1223.         SendODOCToSelf(&stdReply.sfFile);    // so send me an event!
  1224. }
  1225.  
  1226. /*
  1227.     This routine is called during the program's initialization and gives you
  1228.     a chance to allocate or initialize any of your own globals that your
  1229.     dropbox needs.
  1230.     
  1231.     You return a boolean value which determines if you were successful.
  1232.     Returning false will cause DropShell to exit immediately.
  1233. */
  1234. pascal Boolean InitUserGlobals(void)
  1235. {
  1236.     return(true);    // nothing to do, it we must be successful!
  1237. }
  1238.  
  1239. /*
  1240.     This routine is called during the program's cleanup and gives you
  1241.     a chance to deallocate any of your own globals that you allocated 
  1242.     in the above routine.
  1243. */
  1244. pascal void DisposeUserGlobals(void)
  1245. {
  1246.     // nothing to do for our sample dropbox
  1247. }
  1248.